import * as obvm from '../../jsruntime/runtime/vm.mjs'

let messageHandler = {
    virtualServerMessage_NetworkError(evt) {
        let arg = evt.data.arg;
        let targetFsmID = arg.targetFsmID;
        let targetVMID = arg.targetVMID;
        if (targetVMID == this.id) {
            let fsm = this.getFsmByID(targetFsmID);
            if (fsm) {
                fsm.PostMessage(new obvm.EventMessage("NetworkError", 'String', arg.msg, null));
            }
        }
    },
    virtualServerMessage_ServerError(evt) {
        let arg = evt.data.arg;
        let targetFsmID = arg.targetFsmID;
        let targetVMID = arg.targetVMID;
        if (targetVMID == this.id) {
            let fsm = this.getFsmByID(targetFsmID);
            if (fsm) {
                fsm.PostMessage(new obvm.EventMessage("ServiceError", 'String', arg.msg, null));
            }
        }
    },
    virtualServerMessage_Reply(evt) {
        console.log(evt);
        let arg = evt.data.arg;
        let targetFsmID = arg.targetFsmID;
        let targetVMID = arg.targetVMID;
        if (targetVMID == this.id) {
            let fsm = this.getFsmByID(targetFsmID);
            if (fsm) {

                let pfsm = new ProxyFSM(arg.sceneId, evt.source, arg.replyingVMID, arg.replyingFsmID);
                let msg = this.u8arrayToMessage(new Uint8Array(evt.data.arg.msg), () => {
                    return pfsm;
                });
                msg.__virtualClientLib_network = true;
                fsm.PostMessage(msg);
            }
        }
    }
};
function receiveMessage(event) {
    if (!this.isRunning()) {
        window.removeEventListener('message', this.__virtualClientLib_listener, false);
        return;
    }
    let cmd = event.data.cmd;
    if (messageHandler[cmd]) {
        messageHandler[cmd].call(this, event);
    }
}

class ProxyFSM {
    sceneId;
    source; callerVMID; callerFsmID;
    constructor(sceneId, source, callerVMID, callerFsmID) {
        this.sceneId = sceneId;
        this.source = source;
        this.callerVMID = callerVMID;
        this.callerFsmID = callerFsmID;
    }
    toString() {
        return `ProxyFSM{sceneId:${this.sceneId},source:${this.source},callerVMID:${this.callerVMID},callerFsmID:${this.callerFsmID}}`;
    }
    PostMessage(msg, st, uf, local) {
        let callerVMID = msg.sender.VM.id;
        let callerFsmID = msg.sender.id;
        msg = msg.sender.VM.messageToBuffer(msg, st, uf, local);
        let cmd = {
            cmd: 'virtualServerMessage',
            arg: {
                target: this.sceneId,
                callerVMID,
                callerFsmID,
                targetVMID: this.callerVMID,
                targetFsmID: this.callerFsmID,
                msg
            }
        };
        this.source.postMessage(cmd, '*', [msg.buffer]);
    }
}

/**
 * 向服务器门户发送请求
 *
 * @param target {String} 目标地址
 * @param msgArrayBuffer {Uint8Array}  数据
 * @param fsm {obvm.OBFSM} 有限状态机
 * @returns 无返回值
 */
function _sendToServerPortal(target, msgArrayBuffer, fsm, ignoreReply) {

    (window.parent || window).postMessage({
        cmd: 'virtualServerMessage',
        arg: {
            target: target,
            msg: msgArrayBuffer.buffer,
            callerFsmID: fsm.id,
            callerVMID: fsm.VM.id
        }
    }, '*', [msgArrayBuffer.buffer]);
}

function initVM(vm) {
    vm.__virtualClientLib_listener = receiveMessage.bind(vm);
    window.addEventListener("message", vm.__virtualClientLib_listener);
}

function sendMessageToServer(builder, args, ignoreReply) {
    let targetIdx = args[1];
    targetIdx = targetIdx & 0xFFF;
    let targetF = builder.StringRegister(targetIdx);

    let titleIdx = args[2];
    titleIdx = titleIdx & 0xFFF;
    let titleF = builder.StringRegister(titleIdx);

    let dataIdx = args[3];
    let dataRegType = (dataIdx & 0xF000) >> 12;
    dataIdx = dataIdx & 0xFFF;
    let dataF;
    switch (dataRegType) {
        case 0:
            dataF = builder.LongRegister(dataIdx);
            break;
        case 1:
            dataF = builder.DoubleRegister(dataIdx);
            break;
        case 2:
            dataF = builder.StringRegister(dataIdx);
            break;
        case 3:
            dataF = builder.StructRegister(dataIdx);
            break;
        case 4:
            dataF = builder.NObjectRegister(dataIdx);
            break;
    }
    builder.PushAction((st, f, local, pos) => {
        let vm = st.fsm.VM;
        if (!vm.__virtualClientLib_listener) {
            initVM(vm);
        }
        let target = targetF(st, f, local);
        let title = titleF(st, f, local);
        let data = dataF(st, f, local);
        let dataType = obvm.Message.ArgTypeOf(dataRegType, data);
        let msg = new obvm.UserMessage(title, dataType, data, st.fsm);
        let msgBuffer = vm.messageToBuffer(msg, st, f, local);
        _sendToServerPortal(target, msgBuffer, st.fsm, ignoreReply);
        return pos + 1;
    });
}
function sendMessageToServerIgnoreReply(builder, args) {
    sendMessageToServer(builder, args, true);
}
function isNetworkMessage(state, roarfunction, localvar) {
    return !!(state?.currentMessage?.__virtualClientLib_network);
}
export default class VirtualClient {
    static connections = new Map();
    static install(script) {
        script.InstallLib("CSNetwork-Client", "CSNetwork-Client", [
            sendMessageToServer,
            script.NativeUtil.closureReturnValue(isNetworkMessage, 'LongRegister', [], true),
            sendMessageToServerIgnoreReply
        ]);
    }
}